/**
 * @file aes.c
 * @brief T1 AES Driver
 *
 * @author yemt@witsi.cn
 * @date 2011-08-13
 * @version T1.AES.01
 */
#include "t1_crypto.h"
#include "syscfg.h"
#include "iodev.h"
#include "ioctl.h"
#include <string.h>
#include "config.h"


static uint8_t curr_dec = 0;
static inline void aes_copy(uint8_t *dst, uint8_t *src, int length)
{
        dst += length;

        while (length -- > 0) {
                *--dst = *src++;
        }
}

void aes_encrypt(uint8_t *msg, size_t len)
{
        AES->CCFG = AES_ENC_EN;
        aes_copy(AES_IN, msg, len);

        /*
         * AES Encipher key from Cryptography module
         * It is necessary
         */
        SYSCFG->AESKEY_CTL &= ~KEY_SEL_SYS;

        AES->CCFG |= AES_ENC_START;
        while(!(AES->INT_STA & AES_ENC_DONE));
        AES->INT_CLR |= AES_ENC_DONE;
}

void aes_decrypt(uint8_t *cipher, size_t len)
{
        AES->CCFG = AES_DEC_EN;
        aes_copy(AES_IN, cipher, len);

        AES->CCFG |= AES_DEC_LDK;
        while(!(AES->INT_STA & AES_DEC_LDK_DONE));
        AES->INT_CLR |= AES_DEC_LDK_DONE;

        AES->CCFG |= AES_DEC_START;
        while(!(AES->INT_STA & AES_DEC_DONE));
        AES->INT_CLR |= AES_DEC_DONE;

        return;
}


static int aes_init(void)
{
        AES->INT_EN |= AES_DEC_DONE | AES_ENC_DONE | AES_DEC_LDK_DONE;
        AES->INT_MSK &= ~(AES_DEC_DONE | AES_ENC_DONE | AES_DEC_LDK_DONE);

        return 0;
}

static ssize_t aes_read(iodev_t *dev, void *buffer, size_t size)
{
        ssize_t len = size;

        if (size > AES_OUTLEN)
            len = AES_OUTLEN;
        if (!is_enable_crypto_clk()) {
            __enable_crypto_clk();
        }
        aes_copy(buffer, AES_OUT, len);
        memset(AES_OUT, 0x00, AES_OUTLEN);

        return len;
}

static ssize_t aes_write(iodev_t *dev, const void* buffer, size_t size)
{
        ssize_t len = 0;

        if (size < AES_INLEN)
		return 0;
        if (size >= AES_INLEN)
		len = AES_INLEN;
        
        if (!is_enable_crypto_clk()) {
            __enable_crypto_clk();
        }
        if (curr_dec == AES_MODE_DEC) {
                aes_decrypt((uint8_t *)buffer, len);
        }
        else if (curr_dec == AES_MODE_ENC) {
                aes_encrypt((uint8_t *)buffer, len);
        }
        else {
                return 0;
        }

        return len;
}

static int aes_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
        
        switch (cmd) {
        case IOCTL_CRYPTO_SET_KEY:
                if (arg) {
                        if (!is_enable_crypto_clk()) {
                            __enable_crypto_clk();
                        }
                        aes_copy(AES_KEY, (uint8_t *)arg, AES_KEYLEN);
                }
                break;
        case IOCTL_CRYPTO_SET_ENCRYPT:
                curr_dec = AES_MODE_ENC;
                break;
        case IOCTL_CRYPTO_SET_DECRYPT:
                curr_dec = AES_MODE_DEC;
                break;        
        case IOCTL_CRYPTO_START:
                __enable_crypto_clk();
                aes_init();
                break;
        case IOCTL_CRYPTO_STOP:
                __disable_crypto_clk();
                break;
        default:
                break;

        }
        return 0;
}


static const iodev_t T1_aes __iodev_entry = {
        .name = "aes",
        .priv = NULL,
        .init = NULL,
        .read = aes_read,
        .write = aes_write,
        .ioctl = aes_ioctl,
        .poll = NULL,
#ifdef CONFIG_POWER_CTL
        .suspend = NULL,
        .wakeup = NULL,
#endif
};

